package org.orm.hsc;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Date;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author
 * @date 2025-04-11
 * @Description 对新规则进行处理 ， 主要分两部分 <br/>
 * 1. 将新sql 处理成 原始sql。在处理的过程中，存储?对应的参数名 <br/>
 * insert into t_car(cname,color,price) values(null,#{cname},#{color},#{price})
 * insert into t_car(cname,color,price) values(null,?,?,?) + [cname, color, price]
 * 2. 根据解析出来的参数名，从参数对象中获取对应的参数值 组成数组 [bmw ,red ,300000.0]
 */
public class SqlHandler {
    //新规则sql
    private String newSql;
    //原始规则sql
    private String oldSql;
    //装载新sql中 #{}指定的参数名 paramNames = [cno, color]
    private List<String> paramNames;
    //装载根据#{}中的参数名，从参数对象中获得的参数值
    private static final Object[] DEFAULT_PARAM_VALUES = {};
    private Object[] paramValues = DEFAULT_PARAM_VALUES;

    //insert独有
    private String propertyName;

    //select独有
    private Class rowType;

    // 作用：定义一个正则表达式，用于匹配 SQL 字符串中的 #{key} 格式的内容。
    //正则解释：
    // #\\{：匹配 #{，\\ 是转义字符。
    // \\w+：匹配一个或多个字母、数字或下划线（即参数名）。
    // \\}：匹配 }，\\ 是转义字符。
    // 示例：#{cname}、#{color}、#{price} 都会匹配。
    final static Pattern PATTERN = Pattern.compile("#\\{\\w+\\}");

    /**
     * 将sql 中 #{cname} 中的key值提取出来 并且 替代?
     * 方式二：使用正则表达式
     * 使用正则表达式处理sql 将#{cname} 中的key值提取出来 并且 替代?
     * 比下面的字符串截取更加高效
     */
    public void executeSql(String sql) {
        //1、初始化参数名列表：
        paramNames = new ArrayList<>();
        // 2、创建正则匹配器
        Matcher matcher = PATTERN.matcher(sql);
        // 3、循环查找所有匹配的 #{key}
        while (matcher.find()) {
            // 获取匹配的完整字符串，如 #{cname}
            String paramName = matcher.group();
            System.out.println(paramName);
            // 利用正则 去掉 #{ 和 }，提取参数名，如 cname
            paramName = paramName.replaceAll("[#\\{\\}]", "");
            // 将参数名添加到列表中
            paramNames.add(paramName);
        }
        // 4、将所有 #{key} 替换为 ?
        this.oldSql = matcher.replaceAll("?");
        System.out.println("oldSql:" + oldSql);
        System.out.println("paramNames = " + paramNames);
        System.out.println("===========================================================");
    }

    /**
     * 方式一：
     * 使用字符串拼接方式 处理sql 将#{cname} 中的key值提取出来 并且 替代?
     * insert into t_car(cname,color,price) values(null,#{cname},#{color},#{price})
     * 将#{cname} 中的key值提取出来 并且 替代?
     *
     * @param sql
     */
    public void executeSql2(String sql) {
        // insert into t_car(cname,color,price) values(null,#{cname},#{color},#{price})
        //循环遍历，找到每一组#{key}  截取出其中的key部分，同时在sql中将其替换成?
        //记录初始sql
        this.newSql = sql;
        paramNames = new ArrayList();
        while (true) {
            // 进行字符截取操作
            int i1 = sql.indexOf("#{");
            // 代码作用：从指定位置开始查找：查找的起始位置是 i1  返回 "}"的索引信息
            int i2 = sql.indexOf("}", i1);
            // 通过 i1 和 i2 可以确定 #{...} 的完整范围，从而提取出 #{} 中的内容（即参数名）
            if (i1 == -1 || i2 == -1 || i1 > i2) {
                //表示sql中没有完整的#{..}对 表示处理完毕
                break;
            }
            // 继续查找
            // 根据索引位置 获取key 值  #{cname} 中的 cname
            // 为了跳过 #{ 这两个字符，直接从参数名的起始位置开始截取，所以需要 i1+2
            String paramName = sql.substring(i1 + 2, i2).trim();
            // 保存参数名
            paramNames.add(paramName);
            if (i2 == sql.length() - 1) {
                //#{..}右侧没有内容，说明最后一个#{}也处理完毕，替换完?后就可以结束了
                break;
            } else {
                //将#{cname}替换成?
                sql = sql.substring(0, i1) + "?" + sql.substring(i2 + 1);
                // 继续循环处理
                continue;
            }
        }
        // 将sql中所有的#{}都替换成了? ，同时也将参数名都存入了集合
        this.oldSql = sql;
        System.out.println("oldSql:" + oldSql);
        System.out.println("newSql:" + newSql);
        System.out.println("paramNames:" + paramNames);

    }

//    public static void main(String[] args) {
//        SqlHandler sqlHandler = new SqlHandler();
//        sqlHandler.executeSql2("insert into t_car(cname,color,price) values(null,#{cname},#{color},#{price})");
//    }

    /**
     * 根据之前得到的参数名，从传递的参数对象中获得对应的参数值
     *
     * @param paramObj 这个对象有3种可能 （实际上可能更多） <br/>
     *                 是一个domain对象    executeParam(car)<br/>
     *                 是一个map对象  executeParam(map)<br/>
     *                 是一个简单类型对象（值） executeParam(10)<br/>
     */
    public void executeParam(Object paramObj) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        if (paramObj == null) {
            return;
        }
        // 获取对应的参数值
        // 参数值value 与参数个数保持一致
        paramValues = new Object[paramNames.size()];

        // 处理参数对应的类型
        // 处理简单类型

        if (isSimple(paramObj.getClass())) {
            //  判断成立  说明只传递一个值
            for (int i = 0; i < paramValues.length; i++) {
                paramValues[i] = paramObj;
            }
        } else if (paramObj instanceof Map) {  // 处理map 类型
            Map map = (Map) paramObj;
            // 赋值
            for (int i = 0; i < paramNames.size(); i++) {
                String paramName = paramNames.get(i);
                paramValues[i] = map.get(paramName);
            }
        } else {
            // 传入的类型为对象类型
            // 获取当前对象的类对象
            Class<?> clazz = paramObj.getClass();
            for (int i = 0; i < paramValues.length; i++) {
                // 获取参数名称 假设 paramNames = [cno, color]
                // 得到 cno
                String paramName = paramNames.get(i);
                // 根据参数名 计算对应的get方法名 get+C+name
                String methodName;
                if (paramName.length() == 1) {
                    // 拼凑get 方法 如果属性名 只有一个字母 需要单独处理
                    // demo c ----> getC
                    methodName = "get" + paramName.toUpperCase();
                } else {
                    // cname ----> getCname
                    methodName = "get" + paramName.substring(0, 1).toUpperCase() + paramName.substring(1);
                }
                // 获取到对应的get方法
                Method method = clazz.getMethod(methodName);
                // 执行get方法 得到对应的值
                Object value = method.invoke(paramObj);
                // 保存对应的属性值
                paramValues[i] = value;
            }
            // 此时 paramValues 中保存了所有的参数值
        }
        System.out.println("values : " + Arrays.toString(paramValues));


    }

    private boolean isSimple(Class<?> type) {
        return type == int.class || type == Integer.class
                || type == String.class
                || type == long.class || type == Long.class
                || type == double.class || type == Double.class
                || type == Date.class;
    }

    public String getOldSql() {
        return oldSql;
    }

    public void setOldSql(String oldSql) {
        this.oldSql = oldSql;
    }

    public Object[] getParamValues() {
        return paramValues;
    }

    public void setParamValues(Object[] paramValues) {
        this.paramValues = paramValues;
    }

    public String getPropertyName() {
        return propertyName;
    }

    public void setPropertyName(String propertyName) {
        this.propertyName = propertyName;
    }

    public Class getRowType() {
        return rowType;
    }

    public void setRowType(Class rowType) {
        this.rowType = rowType;
    }
}
